using System;
using System.Collections.Generic;
namespace NeuralNetwork
{
	public class Trainer
	{
		
		public static void trainOuputPerceptron (double desiredOutput, Neuron outputPerceptron)
		{
			
			double output = outputPerceptron.output;
			//double error = output * (1 - output) * (desiredOutput - output);
            //new error fuction from http://www.doc.ic.ac.uk/~nd/surprise_96/journal/vol4/cs11/report.html#Appendix B - The back-propagation Algorithm - a mathematical approach

            double error = ((output - desiredOutput) * (output - desiredOutput)) / 2;
			outputPerceptron.error = error;
			//λ (Lambda) the Learning Rate: a real number constant, usually 0.2 for output layer neurons and 0.15 for hidden layer neurons.
			double λ = .2;
			double Δθ = λ * error;
			outputPerceptron.threshold += Δθ;
			foreach (Input input in outputPerceptron.inputs) {
				input.weight += Δθ * input.data;
			}
		
		}
		
		public static void trainHiddenLayerPerceptron (Neuron hiddenLayerPerceptron)
		{
			double output = hiddenLayerPerceptron.output;
			double sumWeightsAndErrors = 0;
			foreach (Input input in hiddenLayerPerceptron.outputs) {
				sumWeightsAndErrors += input.toPerceptron.error * input.weight;
			}
			double error = output * (1 - output) * sumWeightsAndErrors;
			double λ = .15;
			double Δθ = λ * error;
			hiddenLayerPerceptron.threshold += Δθ;
			foreach (Input input in hiddenLayerPerceptron.inputs)
			{
				input.weight += Δθ * input.data;
			}
		}
		//this trains the network 
		public static void trainNetwork (double[] desiredOutputs, Network network)
		{
			if (network.perceptron[network.perceptron.Count-1].name != "Output Layer") {
				System.Console.WriteLine ("This perceptron is messed up!");
				Environment.Exit (1);
			}
			int desiredOutputCounter = 0;
			foreach (Neuron perceptron in network.perceptron[network.perceptron.Count - 1]) {
				trainOuputPerceptron (desiredOutputs[desiredOutputCounter],perceptron);
				desiredOutputCounter++;
			}
			for (int i = network.perceptron.Count - 2; i >= 0; i--) {
				foreach (Neuron perceptron in network.perceptron[i]) {
					trainHiddenLayerPerceptron (perceptron);
				}
			
			}
		}
	}
}

